#include "common.hpp"

static const char *optString = "t:p:m:h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ "max", required_argument, NULL, 'm'},
	{ "people", required_argument, NULL, 'p'},
	{ "trainer", required_argument, NULL, 't'},
	{ 0,0,0,0}
};

int main (int argc, char* const argv[]) {
	// Objetos IPC
	ipc::bounded_counter_with_exceptions lugares( ftok(BASE_NAME,11), ftok(BASE_NAME,10),0 );
	ipc::bounded_counter_with_exceptions entrenadores_disponibles( ftok(BASE_NAME,21), ftok(BASE_NAME,20),0 );
	ipc::bounded_counter_with_exceptions pantalla_gigante_aviso ( ftok(BASE_NAME,27), ftok(BASE_NAME,26),0 );
	
	monitor_reservas_with_exceptions reservas(ftok(BASE_NAME,13),ftok(BASE_NAME,12),0);
	monitor_aparatos_with_exceptions aparatos(ftok(BASE_NAME,15), ftok(BASE_NAME,14),0);

	ipc::messages_queue_with_exceptions cola1 (ftok(BASE_NAME,16) );
	ipc::messages_queue_with_exceptions cola2 (ftok(BASE_NAME,17) );
	ipc::messages_queue_with_exceptions colaRecepcion (ftok(BASE_NAME,18) );
	ipc::messages_queue_with_exceptions colaAtencion (ftok(BASE_NAME,19) );

	ipc::semaphore_with_exceptions control_ingreso ( ftok(BASE_NAME,30),0 );
	ipc::semaphore_with_exceptions pantalla_gigante_mostrar ( ftok(BASE_NAME,25),0 );

	// Parametros.
	long maximo = 10;
	long personas = 0;
	long entrenadores = 0;

	// Variables.
	int longIndex = 0;
	int option = 0;
	bool ayuda = false;
	pid_t arribos = 0;

	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
			case 'm':
				maximo = parse(optarg,0);
				break;
			case 'p':
				personas = parse(optarg,0);
				break;
			case 't':
				entrenadores = parse(optarg,0);
				break;
			default:
				break;
		}
	}

	if ( ayuda || (maximo <= 0) || (personas < 0) || (entrenadores < 0) ) {
		std::cerr << "Proceso para preparación del entorno y ejecución." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h] <-max natural> [-people natural]" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;
		std::cerr << "\t\t-m,--max:\tEspacios disponibles en el gimnasio." << std::endl;
		std::cerr << "\t\t-p,--people:\tCantidad de personas a generar." << std::endl;
		std::cerr << "\t\t-t,--trainer:\tCantidad de entrenadores a generar." << std::endl;

		return (ayuda) ? 0 : -1;
	}

	logger.setColor("\033[0;34m");
	try {
		control_ingreso.get(PERMITS | IPC_CREAT|IPC_EXCL);
		control_ingreso.init(1);

		pantalla_gigante_mostrar.get(PERMITS | IPC_CREAT|IPC_EXCL);
		pantalla_gigante_mostrar.init(1);

		lugares.get(PERMITS | IPC_CREAT|IPC_EXCL);
		lugares.init(0,maximo);

		entrenadores_disponibles.get(PERMITS | IPC_CREAT|IPC_EXCL);
		entrenadores_disponibles.init(0,MAX_ENTRENADORES);

		pantalla_gigante_aviso.get(PERMITS | IPC_CREAT|IPC_EXCL);
		pantalla_gigante_aviso.init(1,1);

		reservas.get(PERMITS | IPC_CREAT|IPC_EXCL);

		// Inicialización de la matriz de reservas.
		reservas.init("1");

		aparatos.get(PERMITS | IPC_CREAT|IPC_EXCL);
		aparatos.init( std::vector<enum eAparato>() );
				
		cola1.get(PERMITS | IPC_CREAT|IPC_EXCL);
		cola2.get(PERMITS | IPC_CREAT|IPC_EXCL);
		colaRecepcion.get(PERMITS | IPC_CREAT|IPC_EXCL);
		colaAtencion.get(PERMITS | IPC_CREAT|IPC_EXCL);

		logger << "Lanzador: Inicio. Gimnasio con capacidad para " << maximo << " personas." << nl;

		static char strTime[11];
		strTime[0] = '\0';
		time_t aux = time(NULL);
		struct tm* timeinfo = localtime ( &aux );
		strftime(strTime,11,"%d/%m/%Y", timeinfo);

		sleep(3);
//		int dia = (timeinfo->tm_yday)%MAX_DIAS;
		utils::logger_stream::multi_line calendario(reservas.calendario());
		logger << "Lanzador: " << calendario << nl;
		
		// Preparo una puerta.
		if (fork() == 0) {
			execlp("./puerta","puerta","-k",c_parse(cola1.getKey()),(char*)NULL);
		}

		// Preparo la otra puerta.
		if (fork() == 0) {
			execlp("./puerta","puerta","-k",c_parse(cola2.getKey()),(char*)NULL);
		}

		// Preparo al recepcionista.
		if (fork() == 0) {
			execlp("./recepcionista","recepcionista","-k",c_parse(colaRecepcion.getKey()),(char*)NULL);
		}

		// Genero los entrenadores.
		logger << "Lanzador: generando " << entrenadores << " entrenadores." << nl;
		for (long i=0; i<entrenadores; i++) {
			if (fork() == 0) {
				std::stringstream nombre;
				nombre << "Entrenador " << (i+1);
				execlp("./entrenador","entrenador","-n",nombre.str().c_str(),"-c","20",(char*)NULL);
			}
		}

		// Comienzo a generar personas.
		if (personas > 0)  {
			logger << "Lanzador: generando " << personas << " personas." << nl;
			arribos = fork();
			if (arribos == 0) {
				execlp("./arribos","arribos","-p",c_parse(personas),(char*)NULL);
			}
			logger << "Lanzador: Proceso generador de arribos lanzado con identificador de proceso: " << arribos << "." << nl;
		}

	} catch (ipc::exception& e) {
		logger << "Lanzador: " << e.what() << nl;
		exit(-1);
	}

	logger << "Lanzador: Fin." << nl;
	return 0;
}

